#pragma once

#include <AVUIEffect.h>
#include <AVUIDependencyPropertyChangedEventArgs.h>

namespace AVUI {
class DependencyProperty;
class PixelShader;
class IClientSubgraphCapture;

class ShaderEffect : public Effect
{
    friend class Visual;
public:
    DECLARE_ELEMENT(ShaderEffect, Effect);

protected:
    ShaderEffect();

    template<int floatRegisterIndex>
    static void PixelShaderConstantCallback(DependencyObject* pDO, DependencyPropertyChangedEventArgs* pArgs)
    {
        ShaderEffect* pEffect = (ShaderEffect*)pDO;
        if (pEffect != NULL)
        {
            pEffect->UpdateShaderConstant(pArgs->get_Property(), pArgs->get_NewValue(), floatRegisterIndex);
        }
    }

    template<int samplerRegisterIndex, SamplingMode::Enum samplingMode>
    static void PixelShaderSamplerCallback(DependencyObject* pDO, DependencyPropertyChangedEventArgs* pArgs)
    {
        ShaderEffect* pEffect = (ShaderEffect*) pDO;
        if (pEffect != NULL && pArgs->get_IsAValueChange())
        {
            pEffect->UpdateShaderSampler(pArgs->get_Property(), pArgs->get_NewValue(), samplerRegisterIndex, samplingMode);
        }
    }

    template<int samplerRegisterIndex, SamplingMode::Enum samplingMode>
    static TRefCountedPtr<DependencyProperty> RegisterPixelShaderSamplerProperty(const char* szDPName, const Type& ownerType)
    {
        RegisterPixelShaderSamplerPropertyNonTemplate(szDPName, ownerType, PixelShaderSamplerCallback<samplerRegisterIndex, samplingMode>);
    }
    TRefCountedPtr<DependencyProperty> RegisterPixelShaderSamplerPropertyNonTemplate(const char* szDPName, const Type& ownerType, PropertyChangedCallback changedCallback);

    void UpdateShaderValue(DependencyProperty* pDP);

    int get_DdxUvDdyUvRegisterIndex();
    void set_DdxUvDdyUvRegisterIndex(int value);

    float get_PaddingBottom();
    void set_PaddingBottom(float value);

    float get_PaddingLeft();
    void set_PaddingLeft(float value);

    float get_PaddingRight();
    void set_PaddingRight(float value);

    float get_PaddingTop();
    void set_PaddingTop(float value);

    TRefCountedPtr<PixelShader> get_PixelShader() { return (PixelShader*)GetValue(get_PixelShaderProperty()).get_Ptr(); }
    void set_PixelShader(PixelShader* pValue) { SetValue(get_PixelShaderProperty(), (Object*)pValue); } 
    DECLARE_DEPENDENCY_PROPERTY(PixelShader);

    TRefCountedPtr<Freezable> CreateInstanceCore() { return (Freezable*)get_Type().CreateInstance().get_Ptr(); }

private:
    struct MilColorF { float r; float g; float b; float a; };
    struct SamplerData { TRefCountedPtr<Brush> m_pBrush; SamplingMode::Enum m_samplingMode; };

    static void StaticConstructor();
    void SetRenderStates(IClientSubgraphCapture* pCapture);

    void UpdateShaderConstant(DependencyProperty* pDP, Object* pNewValue, int registerIndex);
    void UpdateShaderSampler(DependencyProperty* pDP, Object* pNewValue, int registerIndex, SamplingMode::Enum samplingMode);
    void StashInPosition(ItemStructList< Nullable<MilColorF> >* pList, int position, const MilColorF& value, int maxIndex, int* pCount);
    void StashSamplerDataInPosition(int position, SamplerData newSampler, int maxIndex);
    const Type* DetermineShaderConstantType(const Type* pType);
    void ConvertValueToMilColorF(Object* pValue, MilColorF* pNewVal);

    float m_bottomPadding;
    int m_ddxUvDdyUvRegisterIndex;
    int m_floatCount;
    ItemStructList< Nullable<MilColorF> > m_floatRegisters;
    float m_leftPadding;
    float m_rightPadding;
    int m_samplerCount;
    ItemStructList< Nullable<SamplerData> > m_samplerData;
    bool m_sentFirstTime;
    float m_topPadding;
};

}; // namespace AVUI
